package com.ingenico.insider.services.impl;

import java.awt.Color;
import java.awt.Container;
import java.io.IOException;
import java.util.Vector;
import java.util.logging.Logger;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;

import com.ingenico.insider.awt.event.CurvesSelectionListener;
import com.ingenico.insider.nodes.PCurve;
import com.ingenico.insider.nodes.PSampleChannel;
import com.ingenico.insider.swing.CurvesPropertiesApplyAction;
import com.ingenico.insider.swing.CurvesPropertiesReloadAction;
import com.ingenico.insider.swing.JPolyTable;
import com.ingenico.insider.swing.table.ColorCellEditor;
import com.ingenico.insider.swing.table.ColorCellRenderer;
import com.ingenico.insider.swing.table.DoubleCellEditor;
import com.ingenico.insider.swing.table.FloatCellEditor;
import com.ingenico.insider.swing.table.IntegerCellEditor;
import com.ingenico.insider.swing.table.PropertiesTableModel;
import com.ingenico.insider.util.Constants;
import com.ingenico.tools.data.chopping.VersatileChopper;

public final class CurvePropertyControlSupplier {
	private static final Logger _logger = Logger.getLogger(CurvePropertyControlSupplier.class.getCanonicalName());

	/**
	 * Public constant keywords
	 */
	public static final String CURVE_PROPERTIES_KEY = "properties";
	public static final String PROPERTIES_TABLE_BASE = Constants.INSIDER_BASE + Constants.SEPARATOR + Constants.TABLE_KEY + Constants.SEPARATOR + CURVE_PROPERTIES_KEY;

	public static final String NAME_KEY = "property";
	public static final String VALUE_KEY = "value";

	public static final String APPLY_PATH          = CURVE_PROPERTIES_KEY + Constants.SEPARATOR + "apply";
	public static final String RELOAD_PATH         = CURVE_PROPERTIES_KEY + Constants.SEPARATOR + "reload";
	public static final String ONLY_SELECTION_PATH = CURVE_PROPERTIES_KEY + Constants.SEPARATOR + "onlyselection";

	/**
	 * singleton instance
	 */
	private static CurvePropertyControlSupplier instance;

	/**
	 * The Layout Actions used with menu items and buttons
	 */
	private final CurvesPropertiesApplyAction applyCurvesPropertiesAction;
	private final CurvesPropertiesReloadAction reloadCurvesPropertiesAction;

	private final JPanel propertyPanel;
	private final JCheckBox applyOnlySelectedLinesCheckBox;
	private final JPolyTable table;
	private final TableModel model;

	private final Class<?>[] availableSamplerClasses;
	
	/**
	 * private constructor of final class to prevent external instantiation
	 */
	private CurvePropertyControlSupplier () {
		// Create actions
		applyCurvesPropertiesAction = (CurvesPropertiesApplyAction) LocalisationSupplier.getInstance().localize(new CurvesPropertiesApplyAction(), APPLY_PATH);
		reloadCurvesPropertiesAction = (CurvesPropertiesReloadAction) LocalisationSupplier.getInstance().localize(new CurvesPropertiesReloadAction(), RELOAD_PATH);

		// load the list of available sampler classes
		availableSamplerClasses = SubSamplingStrategySupplier.getInstance().getAvailableStrategies().toArray(new Class<?>[4]);

		// Prepare the polymorphic JTable
		Vector<String> colVect = new Vector<String>();
		colVect.add(LocalisationSupplier.getInstance().localize(NAME_KEY));
		colVect.add(LocalisationSupplier.getInstance().localize(VALUE_KEY));
		model = new PropertiesTableModel(colVect, 7 + availableSamplerClasses.length);
		table = new JPolyTable();
		initPolyTable();

		// Prepare property panel
		propertyPanel = new JPanel();
		applyOnlySelectedLinesCheckBox = (JCheckBox)LocalisationSupplier.getInstance().localize(new JCheckBox(), ONLY_SELECTION_PATH);
		initPropertyPanel();

		CurvesControlSupplier.getInstance().addListSelectionListener(new CurvesSelectionListener());

		_logger.fine("Registering curves properties to the dock framework");
		DockableViewsSupplier.getInstance().add(propertyPanel, CURVE_PROPERTIES_KEY);
	}

	private void initPolyTable() {
		table.setModel((AbstractTableModel) model);
		table.setDefaultRenderer(Color.class, new ColorCellRenderer(true));
		table.setDefaultEditor(Color.class, new ColorCellEditor(true));
		table.setDefaultEditor(Double.class, new DoubleCellEditor());
		table.setDefaultEditor(Float.class, new FloatCellEditor());
		table.setDefaultEditor(Integer.class, new IntegerCellEditor());
	}

	private void initPropertyPanel() {
		// Create the scrollable table
		final JScrollPane tableScroller = new JScrollPane(table);

		// Create the buttons Panel with the apply/reload buttons
		final Container buttonsPanel = new Container();
		buttonsPanel.setLayout(new BoxLayout(buttonsPanel, BoxLayout.X_AXIS));
		buttonsPanel.add(Box.createHorizontalGlue());

		final JButton applyButton = new JButton();
		applyButton.setAction(applyCurvesPropertiesAction);
		applyButton.setActionCommand(APPLY_PATH);
		buttonsPanel.add(applyButton);

		final JButton reloadButton = new JButton();
		reloadButton.setAction(reloadCurvesPropertiesAction);
		reloadButton.setActionCommand(RELOAD_PATH);
		buttonsPanel.add(reloadButton);

		// Configure the "Only Selection" CheckBox
// FIXME : See how we can align the check box on the left... the line below DOES NOTHING !!!
//		applyOnlySelectedLinesCheckBox.setAlignmentX(0f);

		propertyPanel.setLayout(new BoxLayout(propertyPanel, BoxLayout.Y_AXIS));
		propertyPanel.add(tableScroller);
		propertyPanel.add(applyOnlySelectedLinesCheckBox);
		propertyPanel.add(Box.createVerticalGlue());
		propertyPanel.add(buttonsPanel);

		// Set the panel visibility according to the current selection
		final Object[] currentCurve = CurvesControlSupplier.getInstance().getSelectedValues();
		if ( (currentCurve != null) && (currentCurve.length == 1) ) {
			propertyPanel.setVisible(true);
		} else {
			propertyPanel.setVisible(false);
		}
	}

	public void setPropertiesToPSampleChannel(final PSampleChannel currentCurve) {
		if (currentCurve == null) {
			return;
		}

		for (int i = 0; i < table.getRowCount(); i++) {
			// If "selectionOnly" is not checked OR if the column is also selected
			/*
			 * We apply the properties only if:
			 * "selectionOnly" is not checked
			 * OR (if it IS selected)
			 * if the column is selected as well
			 */
			if ( ( ! applyOnlySelectedLinesCheckBox.isSelected()) || (table.isRowSelected(i))) {
				switch (i) {
				case 0:
					currentCurve.setVisible((Boolean)model.getValueAt(0, 1));
					break;
				case 1:
					currentCurve.setTransparency((Float)model.getValueAt(1,1));
					break;
				case 2:
					currentCurve.setStrokePaint((Color)model.getValueAt(2,1));
					break;
				case 3:
					final Double offsetX = (Double)model.getValueAt(3, 1);
					currentCurve.setOffsetX(offsetX);
					break;
				case 4:
					final Double offsetY = (Double)model.getValueAt(4, 1);
					currentCurve.setOffsetY(offsetY);
					break;
				case 5:
					currentCurve.setScale(currentCurve.getScaleX(), (Double)model.getValueAt(5,1));
					break;
				case 6:
// FIXME: Implement this !
					break;
				default:
					if ((Boolean)model.getValueAt(i, 1)) {
						currentCurve.addChild(
							SubSamplingStrategySupplier.getInstance().buildStrategy(availableSamplerClasses[i-7]),
							new VersatileChopper()
						);
					} else {
						final PCurve child = currentCurve.getChild(availableSamplerClasses[i-7]);
						if (child != null) {
							currentCurve.removeChild(child);
						}
					}
				}
			}
		}		
	}

	public void showPropertiesFromPSampleChannel(final PSampleChannel currentCurve) {
		if (currentCurve == null) {
			return;
		}

		model.setValueAt("Visible", 0, 0);
		model.setValueAt(currentCurve.getVisible(), 0, 1);

		model.setValueAt("Transparency", 1, 0);
		model.setValueAt(Float.valueOf(currentCurve.getTransparency()), 1, 1);

		model.setValueAt("Color", 2, 0);
		model.setValueAt(currentCurve.getStrokePaint(), 2, 1);

		model.setValueAt("OffsetX", 3, 0);
		model.setValueAt(currentCurve.getOffset().getX(), 3, 1);
		model.setValueAt("OffsetY", 4, 0);
		model.setValueAt(currentCurve.getOffset().getY(), 4, 1);

		model.setValueAt("ScaleY", 5, 0);
		model.setValueAt(currentCurve.getScaleY(), 5, 1);
		
		model.setValueAt("Sample rate", 6, 0);
		try {
			model.setValueAt(currentCurve.getDataChannel().getSampleRate(), 6, 1);
		} catch (IOException e) {
// TODO Auto-generated catch block, when can this occur ? what do we do ?
			e.printStackTrace();
		}

		int lineNumber=7;
		for (Class<?> currentSampler : availableSamplerClasses) {
			model.setValueAt(LocalisationSupplier.getInstance().localize(currentSampler.getCanonicalName()), lineNumber, 0);
			model.setValueAt((currentCurve.getChild(currentSampler) != null), lineNumber, 1);
			++lineNumber;
		}
	}

	
	/**
	 * retrieve the singleton instance
	 *
	 * @return the CurvePropertyControlSupplier singleton
	 */
	public static CurvePropertyControlSupplier getInstance() {
		if (instance == null) {
			_logger.info(CurvePropertyControlSupplier.class.getName() + " instance does not exist... Creating instance.");
			instance = new CurvePropertyControlSupplier();
		}
		return instance;
	}

	/**
	 * Return the table model used to maintain data in the properties list
	 *
	 * @return the table model associated with the JTable
	 */
	public TableModel getModel() {
		return model;
	}

	/**
	 * Check or Uncheck the "ApplyOnlySelectedLines" CheckBox
	 * 
	 * @param onlySelection
	 */
	public void setApplyOnlySelectedLines(boolean onlySelection) {
		applyOnlySelectedLinesCheckBox.setSelected(onlySelection);
	}

	/**
	 * Hide or displays the property panel
	 * 
	 * @param visible true if the panel should be displayed, false otherwise.
	 */
	public void setVisible(boolean visible) {
		_logger.finest(Constants.ENTRY);
		propertyPanel.setVisible(visible);
	}

	public CurvesPropertiesApplyAction getApplyCurvesPropertiesAction() {
		return applyCurvesPropertiesAction;
	}

	public CurvesPropertiesReloadAction getReloadCurvesPropertiesAction() {
		return reloadCurvesPropertiesAction;
	}
}
